GtkBuilder: Added api to allow private signal callbacks
authorTristan Van Berkom <tristanvb@openismus.com>
Wed, 20 Mar 2013 03:48:38 +0000 (12:48 +0900)
committerTristan Van Berkom <tristanvb@openismus.com>
Mon, 8 Apr 2013 12:19:27 +0000 (21:19 +0900)
In preperation for composite objects, for better encapsulation
the following APIs are added to allow handling of signals declared
in the XML with callbacks declared statically.

  o gtk_builder_add_callback_symbol[s]()

    Adds a symbol to the internal symbol hash

  o gtk_builder_lookup_symbol()

    Looks up a symbol, exposed in case added symbols are used
    in conjunction with gtk_builder_connect_signals_full()

The default implementation of gtk_builder_connect_signals() now
does not have a strong requirement on GModule (or a strong requirement
on symbols being declared in the global namespace). Instead GModule
is used as a fallback in the case that symbols are not declared
explicitly.

docs/reference/gtk/gtk3-sections.txt
gtk/gtk.symbols
gtk/gtkbuilder.c
gtk/gtkbuilder.h

index 05cfd5b33607a5bb7dd5219f701d98169aa5af6c..6ab2f340fff1ba44b5947182d48a06148844fcee 100644 (file)
@@ -539,6 +539,9 @@ gtk_builder_get_objects
 gtk_builder_expose_object
 gtk_builder_connect_signals
 gtk_builder_connect_signals_full
+gtk_builder_add_callback_symbol
+gtk_builder_add_callback_symbols
+gtk_builder_lookup_callback_symbol
 gtk_builder_set_translation_domain
 gtk_builder_get_translation_domain
 gtk_builder_get_type_from_name
index 96811a5ff4e7ee5ff0c5ebd1e938039f7af0ab8c..31a2a8bce764d34916826a21fea60dcb610f62bf 100644 (file)
@@ -330,6 +330,8 @@ gtk_buildable_get_type
 gtk_buildable_parser_finished
 gtk_buildable_set_buildable_property
 gtk_buildable_set_name
+gtk_builder_add_callback_symbol
+gtk_builder_add_callback_symbols
 gtk_builder_add_from_file
 gtk_builder_add_from_resource
 gtk_builder_add_from_string
@@ -346,6 +348,7 @@ gtk_builder_get_objects
 gtk_builder_get_translation_domain
 gtk_builder_get_type_from_name
 gtk_builder_get_type
+gtk_builder_lookup_callback_symbol
 gtk_builder_new
 gtk_builder_set_translation_domain
 gtk_builder_value_from_string
index 6ae127ff64fa589acd80e083be75e682f45c50e1..9e2f29466e0453a006647899eb0479fd4b0cf351 100644 (file)
@@ -270,6 +270,7 @@ struct _GtkBuilderPrivate
 {
   gchar *domain;
   GHashTable *objects;
+  GHashTable *callbacks;
   GSList *delayed_properties;
   GSList *signals;
   gchar *filename;
@@ -1390,11 +1391,22 @@ gtk_builder_connect_signals_default (GtkBuilder    *builder,
 {
   GCallback func;
   connect_args *args = (connect_args*)user_data;
-  
-  if (!g_module_symbol (args->module, handler_name, (gpointer)&func))
+
+  func = gtk_builder_lookup_callback_symbol (builder, handler_name);
+
+  if (!func)
     {
-      g_warning ("Could not find signal handler '%s'", handler_name);
-      return;
+      /* Only error out for missing GModule support if we've not
+       * found the symbols explicitly added with gtk_builder_add_callback_symbol()
+       */
+      if (args->module == NULL)
+       g_error ("gtk_builder_connect_signals() requires working GModule");
+  
+      if (!g_module_symbol (args->module, handler_name, (gpointer)&func))
+       {
+         g_warning ("Could not find signal handler '%s'", handler_name);
+         return;
+       }
     }
 
   if (connect_object)
@@ -1410,14 +1422,20 @@ gtk_builder_connect_signals_default (GtkBuilder    *builder,
  * @user_data: a pointer to a structure sent in as user data to all signals
  *
  * This method is a simpler variation of gtk_builder_connect_signals_full().
- * It uses #GModule's introspective features (by opening the module %NULL) 
+ * It uses symbols explicitly added to @builder with prior calls to
+ * gtk_builder_add_callback_symbol(). In the case that symbols are not
+ * explicitly added; it uses #GModule's introspective features (by opening the module %NULL) 
  * to look at the application's symbol table. From here it tries to match
  * the signal handler names given in the interface description with
  * symbols in the application and connects the signals. Note that this
  * function can only be called once, subsequent calls will do nothing.
  * 
- * Note that this function will not work correctly if #GModule is not
- * supported on the platform.
+ * Note that unless gtk_builder_add_callback_symbol() is called for
+ * all signal callbacks which are referenced by the loaded XML, this 
+ * function will require that #GModule be supported on the platform.
+ * 
+ * If you rely on #GModule support to lookup callbacks in the symbol table,
+ * the following details should be noted:
  *
  * When compiling applications for Windows, you must declare signal callbacks
  * with #G_MODULE_EXPORT, or they will not be put in the symbol table.
@@ -1435,17 +1453,17 @@ gtk_builder_connect_signals (GtkBuilder *builder,
   
   g_return_if_fail (GTK_IS_BUILDER (builder));
   
-  if (!g_module_supported ())
-    g_error ("gtk_builder_connect_signals() requires working GModule");
-
   args = g_slice_new0 (connect_args);
-  args->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
   args->data = user_data;
+
+  if (g_module_supported ())
+    args->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
   
   gtk_builder_connect_signals_full (builder,
                                     gtk_builder_connect_signals_default,
                                     args);
-  g_module_close (args->module);
+  if (args->module)
+    g_module_close (args->module);
 
   g_slice_free (connect_args, args);
 }
@@ -2122,3 +2140,108 @@ _gtk_builder_get_absolute_filename (GtkBuilder *builder, const gchar *string)
   
   return filename;
 }
+
+/**
+ * gtk_builder_add_callback_symbol:
+ * @builder: a #GtkBuilder
+ * @callback_name: The name of the callback, as expected in the XML
+ * @callback_symbol: (scope async): The callback pointer
+ *
+ * Adds the @callback_symbol to the scope of @builder under the given @callback_name.
+ *
+ * Using this function overrides the behavior of gtk_builder_connect_signals()
+ * for any callback symbols that are added. Using this method allows for better
+ * encapsulation as it does not require that callback symbols be declared in
+ * the global namespace.
+ *
+ * Since: 3.10
+ */
+void
+gtk_builder_add_callback_symbol (GtkBuilder    *builder,
+                                const gchar   *callback_name,
+                                GCallback      callback_symbol)
+{
+  g_return_if_fail (GTK_IS_BUILDER (builder));
+  g_return_if_fail (callback_name && callback_name[0]);
+  g_return_if_fail (callback_symbol != NULL);
+
+  if (!builder->priv->callbacks)
+    builder->priv->callbacks = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                     g_free, NULL);
+
+  g_hash_table_insert (builder->priv->callbacks, g_strdup (callback_name), callback_symbol);
+}
+
+/**
+ * gtk_builder_add_callback_symbols:
+ * @builder: a #GtkBuilder
+ * @first_callback_name: The name of the callback, as expected in the XML
+ * @first_callback_symbol: (scope async): The callback pointer
+ * @...: A list of callback name and callback symbol pairs terminated with %NULL
+ *
+ * A convenience function to add many callbacks instead of calling
+ * gtk_builder_add_callback_symbol() for each symbol.
+ *
+ * Since: 3.10
+ */
+void
+gtk_builder_add_callback_symbols (GtkBuilder    *builder,
+                                 const gchar   *first_callback_name,
+                                 GCallback      first_callback_symbol,
+                                 ...)
+{
+  va_list var_args;
+  const gchar *callback_name;
+  GCallback callback_symbol;
+
+  g_return_if_fail (GTK_IS_BUILDER (builder));
+  g_return_if_fail (first_callback_name && first_callback_name[0]);
+  g_return_if_fail (first_callback_symbol != NULL);
+
+  callback_name = first_callback_name;
+  callback_symbol = first_callback_symbol;
+
+  va_start (var_args, first_callback_symbol);
+
+  do {
+
+    gtk_builder_add_callback_symbol (builder, callback_name, callback_symbol);
+
+    callback_name = va_arg (var_args, const gchar*);
+
+    if (callback_name)
+      callback_symbol = va_arg (var_args, GCallback);
+
+  } while (callback_name != NULL);
+
+  va_end (var_args);
+}
+
+/**
+ * gtk_builder_lookup_callback_symbol:
+ * @builder: a #GtkBuilder
+ * @callback_name: The name of the callback
+ *
+ * Fetches a symbol previously added to @builder
+ * with gtk_builder_add_callback_symbols()
+ *
+ * This function is intended for possible use in language bindings
+ * or for any case that one might be cusomizing signal connections
+ * using gtk_builder_connect_signals_full()
+ *
+ * Returns: The callback symbol in @builder for @callback_name, or %NULL
+ *
+ * Since: 3.10
+ */
+GCallback
+gtk_builder_lookup_callback_symbol (GtkBuilder    *builder,
+                                   const gchar   *callback_name)
+{
+  g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (callback_name && callback_name[0], NULL);
+
+  if (!builder->priv->callbacks)
+    return NULL;
+
+  return g_hash_table_lookup (builder->priv->callbacks, callback_name);
+}
index 31e5a24039b7a19556811413b609d1b334684a1b..5b22eb13cb8d8fa22295baf70a4ccca6353f4085 100644 (file)
@@ -167,6 +167,19 @@ gboolean     gtk_builder_value_from_string_type  (GtkBuilder    *builder,
                                                   GValue               *value,
                                                  GError       **error);
 
+GDK_AVAILABLE_IN_3_10
+void         gtk_builder_add_callback_symbol     (GtkBuilder    *builder,
+                                                 const gchar   *callback_name,
+                                                 GCallback      callback_symbol);
+GDK_AVAILABLE_IN_3_10
+void         gtk_builder_add_callback_symbols    (GtkBuilder    *builder,
+                                                 const gchar   *first_callback_name,
+                                                 GCallback      first_callback_symbol,
+                                                 ...) G_GNUC_NULL_TERMINATED;
+GDK_AVAILABLE_IN_3_10
+GCallback    gtk_builder_lookup_callback_symbol  (GtkBuilder    *builder,
+                                                 const gchar   *callback_name);
+
 /**
  * GTK_BUILDER_WARN_INVALID_CHILD_TYPE:
  * @object: the #GtkBuildable on which the warning ocurred